#include <stdlib.h>
#include <math.h>
#include "CapacitorPowerModel.h"
#include "../HSGlobal.h"
#include "../Scheduler/Scheduler.h"

CapacitorPowerModel::CapacitorPowerModel() : PowerModel(){

  // TODO: don't hard-code these electrical parameters
  capacitance = 1e-4; // 100 uF
  resurrectionVoltage = 1.8;
  minVoltage = 1.4;
  voltage = initialVoltage = 2.0;
  currentlyDead = false;

  modeResistances[MODE_ACTIVE] = 2e4; // TODO: replace with something reasonable
  modeResistances[MODE_SLEEPING] = 1.801e6;
  modeResistances[MODE_OFF] = 1e9; // TODO: replace with something reasonable

  vtrace = new VoltageTrace();
  char const *traceFilename = getenv("HARVSIM_TRACE_FILE");
  if (traceFilename == 0)
    throw std::exception();

  if (vtrace->loadFromFile(traceFilename) < 0)
    throw std::exception();

  setPowerMode(MODE_ACTIVE);
}

char *CapacitorPowerModel::Identify(){
  return (char *)"Capacitor Power Model";
}

void CapacitorPowerModel::dumpStats(FILE *f){
  HSLOG(f,"Stats for %s\n", this->Identify());
  HSLOG(f,"Initial voltage: %1.8f V\n", initialVoltage);
  HSLOG(f,"Final voltage: %1.8f V\n", voltage);
}


PowerModel::QueryResult *CapacitorPowerModel::tick(unsigned long currentTick,float powerInWatts){

  if( powerInWatts == 0 ){
    
    resistance = modeResistances[MODE_ACTIVE]; 

  }else{

    //Def'n of Watt:
    //P = IV
    //I = P / V
    float P = powerInWatts;
    float V = voltage;
    float I = P / V;
  
    //Ohm's Law:
    //V = IR
    //R = V/I
    float R = V / I;
  
    resistance = R + modeResistances[MODE_ACTIVE]; 
    //fprintf(stderr,"Setting resistance to %f amps\n",resistance);  
 
  } 

  return tick(currentTick);

}

PowerModel::QueryResult *CapacitorPowerModel::tick(unsigned long currentTick){
  static unsigned long printCounter = CAP_LOG_EVERY - 1;

  // Recalculate the voltage based on the previous voltage and the power mode.
  double dt = TICKLENGTH;

  // decay for one timestep (V_final = V_initial * exp(-t / RC))
  voltage = voltage *
    exp((-1.0 * dt * CAP_FUDGE_FACTOR) /
        (resistance * capacitance));

  if (++printCounter == CAP_LOG_EVERY) {
      HSLOG(stderr, "Voltage: %1.8f V\n", voltage);
      printCounter = 0;
  }

  // Consult the energy trace and add cap voltage if trace voltage increased
  // since the last tick.
  double time = currentTick * dt;
  double prevV = vtrace->lastVoltageReturned;
  double newV = vtrace->getVoltage(time);
  if (newV > prevV) {
      // add voltage (V_final = V_initial + (1 - exp(-t / RC)))
      double dv = newV * (1.0 - exp((-dt) / (resistance * capacitance)));
      voltage += 1e7 * dv; // XXX fudge
      //HSLOG(stderr, "At time %1.8f s (V_trace=%1.8f), added %1.8f V -> %1.8f\n", time, newV, dv, voltage);
  }

  if (this->currentlyDead) {
    if (voltage >= resurrectionVoltage) {
      HSLOG(stderr, "Back from the dead at time %1.8f\n", time);
      setPowerMode(MODE_ACTIVE);
      this->queryResult->dead = this->currentlyDead = false;
      this->currDeathTickCount = 0;
    }
  } else {
    if (voltage < minVoltage) {
      HSLOG(stderr, "Dying at time %1.8f\n", time);
      setPowerMode(MODE_OFF);
      this->queryResult->dead = this->currentlyDead = true;
      this->currDeathTickCount = 0;
    }
  }

  return this->queryResult;
}

void CapacitorPowerModel::setPowerMode(capacitor_powermode_t newMode) {
  resistance = modeResistances[newMode];
}

CapacitorPowerModel::~CapacitorPowerModel(){


}

extern "C" PowerModel *Create(){

  return new CapacitorPowerModel();

}
